package developer.算法.回溯.N皇后;

import java.util.*;

/**
 * @author zhangyongkang
 * @time 2025/4/2 11:06
 * @description 按照国际象棋的规则，皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
 * <p>
 * n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上，并且使皇后彼此之间不能相互攻击。
 * <p>
 * 给你一个整数 n ，返回所有不同的 n 皇后问题 的解决方案。
 * <p>
 * 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案，该方案中 'Q' 和 '.' 分别代表了皇后和空位。
 * <p>
 * 输入：n = 4
 * 输出：[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
 * 解释：如上图所示，4 皇后问题存在两个不同的解法。
 * 示例 2：
 * <p>
 * 输入：n = 1
 * 输出：[["Q"]]
 */
public class NQueen {

    public static void main(String[] args) {
        Solution solution = new Solution();
        List<List<String>> lists = solution.solveNQueens(50);
        for (List<String> list : lists) {
            System.out.println("----------------");
            list.forEach(System.out::println);
        }
    }

    static class Solution {
        List<String> current;
        List<List<String>> result;

        public List<List<String>> solveNQueens(int n) {
            current = new ArrayList<>();
            result = new ArrayList<>();
            dfs(0, n, 0, n);
            return result;
        }

        private void dfs(int start, int end, int level, int n) {
            if (level == end && current.size() == n) {
                result.add(new ArrayList<>(current));
                return;
            }
            for (int i = 0; i < n; i++) {
                if (!hasAttack(i, n)) {
                    String line = buildLine(i, n);
                    current.add(line);
                    dfs(0, end, level + 1, n);
                    current.remove(current.size() - 1);
                }
            }

        }

        private boolean hasAttack(int i, int n) {
            //确认所有攻击点的坐标
            List<int[]> safari = new ArrayList<>();
            //纵向（横向不考虑，一行只放置一个)
            for (int j = 0; j < current.size(); j++) {
                safari.add(new int[]{j, i});
            }
            //斜向
            for (int j = current.size() - 1; j >= 0; j--) {
                int size = current.size();
                //左斜
                int l = i - (size - j);
                if (isInTheGrid(j, l, n)) {
                    safari.add(new int[]{j, l});
                }
                int r = i + (size - j);
                if (isInTheGrid(j, r, n)) {
                    safari.add(new int[]{j, r});
                }
            }

            for (int[] ints : safari) {
                int row = ints[0];
                int clo = ints[1];

                char c = current.get(row).charAt(clo);
                if (c == 'Q') {
                    return true;
                }
            }
            return false;
        }

        private boolean isInTheGrid(int row, int col, int n) {
            return row >= 0 && row < n && col >= 0 && col < n;
        }

        private String buildLine(int idx, int n) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < n; i++) {
                if (i == idx) {
                    sb.append("Q");
                } else {
                    sb.append(".");
                }
            }
            return sb.toString();
        }
    }

    /**
     * 作者：力扣官方题解
     * 链接：https://leetcode.cn/problems/n-queens/solutions/398929/nhuang-hou-by-leetcode-solution/
     * 来源：力扣（LeetCode）
     * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
     */
    class SolutionOfficial {
        public List<List<String>> solveNQueens(int n) {
            List<List<String>> solutions = new ArrayList<List<String>>();
            int[] queens = new int[n];
            Arrays.fill(queens, -1);
            Set<Integer> columns = new HashSet<Integer>();
            Set<Integer> diagonals1 = new HashSet<Integer>();
            Set<Integer> diagonals2 = new HashSet<Integer>();
            backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
            return solutions;
        }

        public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
            if (row == n) {
                List<String> board = generateBoard(queens, n);
                solutions.add(board);
            } else {
                for (int i = 0; i < n; i++) {
                    if (columns.contains(i)) {
                        continue;
                    }
                    int diagonal1 = row - i;
                    if (diagonals1.contains(diagonal1)) {
                        continue;
                    }
                    int diagonal2 = row + i;
                    if (diagonals2.contains(diagonal2)) {
                        continue;
                    }
                    queens[row] = i;
                    columns.add(i);
                    diagonals1.add(diagonal1);
                    diagonals2.add(diagonal2);
                    backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
                    queens[row] = -1;
                    columns.remove(i);
                    diagonals1.remove(diagonal1);
                    diagonals2.remove(diagonal2);
                }
            }
        }

        public List<String> generateBoard(int[] queens, int n) {
            List<String> board = new ArrayList<String>();
            for (int i = 0; i < n; i++) {
                char[] row = new char[n];
                Arrays.fill(row, '.');
                row[queens[i]] = 'Q';
                board.add(new String(row));
            }
            return board;
        }
    }

}
